package com.ruibang.glass.quality.util.spc;

import java.math.BigDecimal;
import java.math.MathContext;
import java.util.ArrayList;
import java.util.List;

/**
 * @author 宋锦康  SPC工具类
 */
public class SPCUtils {

    /**
     * @Description: 计算平均值
     * @version v1.0
     * @author songJinKang
     * @date 2023-10-18 16:01
     */
    public static double calculateMean(double[] data) {
        double sum = 0;
        for (double d : data) {
            sum += d;
        }
        return sum / data.length;
    }

    /**
     * @Description: 计算平均值
     * @version v1.0
     * @author songJinKang
     * @date 2023-10-18 16:01
     */
    public static double calculateMean(List<Double> data) {
        return data.stream().mapToDouble(Double::doubleValue).average().orElse(0.0);
    }


    /**
     * @Description: 计算标准差
     * @version v1.0
     * @author songJinKang
     * @date 2023-10-18 16:01
     */
    public static double calculateStandardDeviation(double[] data) {
        double mean = calculateMean(data);
        double sum = 0;
        for (double value : data) {
            sum += Math.pow(value - mean, 2);
        }
        return Math.sqrt(sum / (data.length - 1));
    }

    /**
     * @Description: 计算标准差
     * @version v1.0
     * @author songJinKang
     * @date 2023-10-18 16:01
     */
    public static double calculateStandardDeviation(List<Double> data) {
        double mean = calculateMean(data);
        double sum = 0;
        for (double value : data) {
            sum += Math.pow(value - mean, 2);
        }
        return Math.sqrt(sum / (data.size() - 1));
    }


    /**
     * @Description: 计算整体标准差
     * @version v1.0
     * @author songJinKang
     * @date 2023-10-19 14:37
     */
    public static double calculateOverallStandardDeviation(double[] data) {
        double mean = calculateMean(data);
        double sumOfSquaredDifferences = 0;
        for (double value : data) {
            double difference = value - mean;
            sumOfSquaredDifferences += difference * difference;
        }
        return Math.sqrt(sumOfSquaredDifferences / data.length);
    }


    /**
     * @Description: 计算样本标准差
     * @version v1.0
     * @author songJinKang
     * @date 2023-10-18 16:00
     */
    public static double calculateSampleStandardDeviation(double[] data) {
        double mean = calculateMean(data);
        double sum = 0;
        for (double value : data) {
            sum += Math.pow(value - mean, 2);
        }
        return Math.sqrt(sum / (data.length - 1));
    }

    /**
     * @Description: 计算平均移动距离 MAD
     * @version v1.0
     * @author songJinKang
     * @date 2023-10-18 16:00
     */
    public static double calculateMeanAbsoluteDeviation(double[] data) {
        double sum = 0.0;
        double mean = calculateMean(data);

        for (double num : data) {
            sum += Math.abs(num - mean);
        }

        return sum / data.length;
    }


    /**
     * @Description: 计算西格玛水平
     * @version v1.0
     * @author songJinKang
     * @date 2023-10-18 16:03
     */
    public static double calculateSigmaLevel(double cp) {
        return (cp <= 0.0) ? Double.NaN :
                (1 - ErrorFunction.erf(ErrorFunction.calculateZ(cp))) * 1000000 / 1000;
    }


    // 计算 CP
    public static double calculateCP(double standardDeviation, double specificationWidth) {
        return specificationWidth / (6 * standardDeviation);
    }

    // 计算 CPL
    public static double calculateCPL(double mean, double lsl, double standardDeviation) {
        return (mean - lsl) / (3 * standardDeviation);
    }

    // 计算 CPU
    public static double calculateCPU(double mean, double usl, double standardDeviation) {
        return (usl - mean) / (3 * standardDeviation);
    }

    // 计算 CPK
    public static double calculateCPK(double mean, double usl, double lsl, double standardDeviation) {
        double cpu = calculateCPU(mean, usl, standardDeviation);
        double cpl = calculateCPL(mean, lsl, standardDeviation);
        return Math.min(cpu, cpl);
    }


    // 计算移动极差
    public static List<Double> calculateMovingRanges(double[] data) {
        List<Double> movingRanges = new ArrayList<>();
        //i等于移动位置
        for (int i = 1; i < data.length; i++) {
            double movingRange = Math.abs(data[i] - data[i - 1]);
            movingRanges.add(movingRange);
        }
        return movingRanges;
    }


    /**
     * 计算标准差σ=sqrt(s^2)
     *
     * @return
     */
    public static BigDecimal standardDiviation(List<BigDecimal> datas) {
        int n = datas.size();
        BigDecimal sum = datas.stream().reduce(BigDecimal.ZERO, BigDecimal::add);
        BigDecimal avg = sum.divide(new BigDecimal(n), 6, BigDecimal.ROUND_HALF_UP);
        //方差s^2=[(x1-x)^2 +...(xn-x)^2]/n 或者s^2=[(x1-x)^2 +...(xn-x)^2]/(n-1)
        BigDecimal dVar = BigDecimal.ZERO;
        for (int i = 0; i < n; i++) {
            dVar = dVar.add((datas.get(i).subtract(avg)).multiply(datas.get(i).subtract(avg)));
        }
        BigDecimal v = dVar.divide(new BigDecimal(n - 1), 6, BigDecimal.ROUND_HALF_UP);
        if (v.compareTo(BigDecimal.ZERO) == 0) {
            return BigDecimal.ZERO;
        }
        return sqrt(v);
    }


    // BigDecimal的开方
    private static BigDecimal sqrt(BigDecimal num) {
        if (num.compareTo(BigDecimal.ZERO) < 0) {
            return BigDecimal.ZERO;
        }
        BigDecimal x = num.divide(new BigDecimal("2"), MathContext.DECIMAL128);
        while (x.subtract(x = sqrtIteration(x, num)).abs().compareTo(new BigDecimal("0.0000000000000000000001")) > 0) ;
        return x;
    }

    private static BigDecimal sqrtIteration(BigDecimal x, BigDecimal n) {
        return x.add(n.divide(x, MathContext.DECIMAL128)).divide(new BigDecimal("2"), MathContext.DECIMAL128);
    }

    /**
     * 计算平均值
     *
     * @param datas
     * @return
     */
    public static BigDecimal average(List<BigDecimal> datas) {
        BigDecimal sum = datas.stream().reduce(BigDecimal.ZERO, BigDecimal::add);
        return divide(sum, BigDecimal.valueOf(datas.size()));
    }

    private static BigDecimal divide(BigDecimal a, BigDecimal b) {
        if (b.compareTo(BigDecimal.ZERO) == 0) {
            return BigDecimal.ZERO;
        }
        return a.divide(b, 6, BigDecimal.ROUND_HALF_UP);
    }


    public static double cp(double usl, double lsl, double sigma) {
        return (usl - lsl) / (6 * sigma);
    }

    public static double cpl(double mean, double lsl, double sigma) {
        return (mean - lsl) / (3 * sigma);
    }

    public static double cpu(double usl, double mean, double sigma) {
        return (usl - mean) / (3 * sigma);
    }

    public static double cpk(double usl, double lsl, double mean, double sigma) {
        double cpl = cpl(mean, lsl, sigma);
        double cpu = cpu(usl, mean, sigma);
        return Math.min(cpl, cpu);
    }



}
